use cookie::Cookie;
use rinja::Template;
use salvo::oapi::extract::*;
use salvo::prelude::*;
use serde::{Deserialize, Serialize};

use crate::hoops::jwt;
use crate::{json_ok, service, utils, AppResult, JsonResult};

#[handler]
pub async fn login_page(res: &mut Response) -> AppResult<()> {
    #[derive(Template)]
    #[template(path = "login.html")]
    struct LoginTemplate {}
    if let Some(cookie) = res.cookies().get("jwt_token") {
        let token = cookie.value().to_string();
        if jwt::decode_token(&token) {
            res.render(Redirect::other("/users"));
            return Ok(());
        }
    }
    let hello_tmpl = LoginTemplate {};
    res.render(Text::Html(hello_tmpl.render().unwrap()));
    Ok(())
}

#[derive(Deserialize, Default, Debug, ToSchema)]
/// Example:
/// ```json
/// {
///   "username": "admin",
///   "password": "password123"
/// }
/// ```
pub struct LoginInData {
    /// Example: "admin"
    pub username: String,
    /// Example: "password123"
    pub password: String,
}

#[derive(Serialize, Default, Debug, ToSchema)]
/// Example:
/// ```json
/// {
///   "id": "1",
///   "username": "admin",
///   "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
///   "exp": 1672531200
/// }
/// ```
pub struct LoginOutData {
    /// Example: "1"
    pub id: i64,
    /// Example: "admin"
    pub username: String,
    /// Example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    pub token: String,
    /// Example: 1672531200
    pub exp: i64,
}
#[endpoint(tags("auth"))]
pub async fn post_login(
    req: &mut Request,
    idata: JsonBody<LoginInData>,
    res: &mut Response,
) -> JsonResult<LoginOutData> {
    let idata = idata.into_inner();
    let db = service::pool();
    let tenant_id = crate::utils::tenant::get_tenant_id(req)?;
    let user = service::rbac::user::UserService::find_by_username(db, &idata.username, tenant_id)
        .await?
        .ok_or_else(|| {
            tracing::error!("User does not exist.");
            StatusError::unauthorized().brief("User does not exist.")
        })?;

    if utils::verify_password(&idata.password, &user.password).is_err() {
        tracing::error!("Password is incorrect.");
        return Err(StatusError::unauthorized()
            .brief("Addount not exist or password is incorrect.")
            .into());
    }
    let id = user.id;
    let username = user.username;
    let (token, exp) = jwt::get_token(id.to_string())?;
    let odata = LoginOutData {
        id,
        username,
        token,
        exp,
    };
    let cookie = Cookie::build(("jwt_token", odata.token.clone()))
        .path("/")
        .http_only(true)
        .build();
    res.add_cookie(cookie);
    json_ok(odata)
}
